package com.dyna.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.dyna.constants.ModuleErrorCodeConstants;
import com.dyna.core.exception.CustomException;
import com.dyna.core.utils.CollUtil;
import com.dyna.core.utils.ObjectUtil;
import com.dyna.core.utils.SecurityUtil;
import com.dyna.foundation.convert.AuthPermissionConvert;
import com.dyna.foundation.domain.bo.UserInfoBo;
import com.dyna.foundation.domain.bo.po.SysRolePo;
import com.dyna.foundation.domain.entity.SysMenu;
import com.dyna.foundation.domain.entity.SysRoleMenu;
import com.dyna.foundation.domain.entity.SysUserRole;
import com.dyna.foundation.domain.resp.SysPermissionInfoResp;
import com.dyna.mapper.SysRoleMenuMapper;
import com.dyna.mapper.SysUserRoleMapper;
import com.dyna.service.SysMenuService;
import com.dyna.service.SysPermissionService;
import com.dyna.service.SysRoleService;
import com.dyna.service.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@Service
public class SysPermissionServiceImpl implements SysPermissionService {

    @Lazy
    @Resource
    private SysUserService sysUserService;
    @Lazy
    @Resource
    private SysMenuService sysMenuService;
    @Lazy
    @Resource
    private SysUserRoleMapper userRoleMapper;
    @Lazy
    @Resource
    private SysRoleMenuMapper roleMenuMapper;
    @Lazy
    @Resource
    private SysRoleService sysRoleService;

    @Override
    public SysPermissionInfoResp getPermissionInfo() {
        // 1.1 获得用户信息
        Long userId = SecurityUtil.getUserId();
        UserInfoBo userInfoBo = sysUserService.getUserById(userId);
        if (ObjectUtil.isNull(userInfoBo)) {
            throw new CustomException(ModuleErrorCodeConstants.USER_NOT_EXISTS.getMsg());
        }
        if (CollUtil.isEmpty(userInfoBo.getRoleList())) {
            SysPermissionInfoResp.UserVO userVO = BeanUtil.copyProperties(userInfoBo, SysPermissionInfoResp.UserVO.class);
            return new SysPermissionInfoResp().setUser(userVO);
        }
        // 1.2 获取角色菜单权限
        List<Long> roleIds = userInfoBo.getRoleList().stream().map(SysRolePo::getId).distinct().collect(Collectors.toList());
        List<SysMenu> menuList = sysMenuService.getSysMenuListByRoleIds(roleIds);
        return AuthPermissionConvert.convert(userInfoBo, menuList);
    }

    @Override
    @DSTransactional // 多数据源，使用 @DSTransactional 保证本地事务，以及数据源的切换
    public void assignUserRole(Long userId, Set<Long> roleIds) {
        // 获得角色拥有角色编号
        Set<Long> dbRoleIds = CollUtil.convertSet(userRoleMapper.selectListByUserId(userId),
                SysUserRole::getRoleId);
        // 计算新增和删除的角色编号
        Set<Long> roleIdList = CollUtil.emptyIfNull(roleIds);
        Collection<Long> createRoleIds = CollUtil.subtract(roleIdList, dbRoleIds);
        Collection<Long> deleteMenuIds = CollUtil.subtract(dbRoleIds, roleIdList);
        // 执行新增和删除。对于已经授权的角色，不用做任何处理
        if (!CollectionUtil.isEmpty(createRoleIds)) {
            userRoleMapper.insertBatch(CollUtil.convertList(createRoleIds, roleId -> {
                SysUserRole entity = new SysUserRole();
                entity.setUserId(userId);
                entity.setRoleId(roleId);
                return entity;
            }));
        }
        if (!CollectionUtil.isEmpty(deleteMenuIds)) {
            userRoleMapper.deleteListByUserIdAndRoleIdIds(userId, deleteMenuIds);
        }
    }

    @Override
    @DSTransactional // 多数据源，使用 @DSTransactional 保证本地事务，以及数据源的切换
    public void assignRoleMenu(Long roleId, Set<Long> menuIds) {
        // 获得角色拥有菜单编号
        Set<Long> dbMenuIds = CollUtil.convertSet(roleMenuMapper.selectListByRoleId(roleId), SysRoleMenu::getMenuId);
        // 计算新增和删除的菜单编号
        Set<Long> menuIdList = CollUtil.emptyIfNull(menuIds);
        Collection<Long> createMenuIds = CollUtil.subtract(menuIdList, dbMenuIds);
        Collection<Long> deleteMenuIds = CollUtil.subtract(dbMenuIds, menuIdList);
        // 执行新增和删除。对于已经授权的菜单，不用做任何处理
        if (cn.hutool.core.collection.CollUtil.isNotEmpty(createMenuIds)) {
            roleMenuMapper.insertBatch(CollUtil.convertList(createMenuIds, menuId -> {
                SysRoleMenu entity = new SysRoleMenu();
                entity.setRoleId(roleId);
                entity.setMenuId(menuId);
                return entity;
            }));
        }
        if (cn.hutool.core.collection.CollUtil.isNotEmpty(deleteMenuIds)) {
            roleMenuMapper.deleteListByRoleIdAndMenuIds(roleId, deleteMenuIds);
        }
    }

    @Override
    public Set<Long> getRoleMenuListByRoleId(Collection<Long> roleIds) {
        if (cn.hutool.core.collection.CollUtil.isEmpty(roleIds)) {
            return Collections.emptySet();
        }

        // 如果是管理员的情况下，获取全部菜单编号
        if (sysRoleService.hasAnySuperAdmin(roleIds)) {
            return CollUtil.convertSet(sysMenuService.getMenuList(), SysMenu::getId);
        }
        // 如果是非管理员的情况下，获得拥有的菜单编号
        return CollUtil.convertSet(roleMenuMapper.selectListByRoleId(roleIds), SysRoleMenu::getMenuId);
    }

    @Override
    public void processUserDeleted(Long userId) {
        userRoleMapper.deleteListByUserId(userId);
    }

    @Override
    public Set<Long> getUserRoleIdListByRoleId(Collection<Long> roleIds) {
        return CollUtil.convertSet(userRoleMapper.selectListByRoleIds(roleIds), SysUserRole::getUserId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processRoleDeleted(Long roleId) {
        // 标记删除 UserRole
        userRoleMapper.deleteListByRoleId(roleId);
        // 标记删除 RoleMenu
        roleMenuMapper.deleteListByRoleId(roleId);
    }

    @Override
    public void processMenuDeleted(Long menuId) {
        roleMenuMapper.deleteListByMenuId(menuId);
    }


}
